﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TerhelesEloszto.Common;
using System.Data.SqlClient;
using System.Transactions;
using GraphAlgorithm;

enum Role { user, admin, teacher, both };

namespace TerhelesEloszto.Engine
{
	/// <summary>
	/// Az adatbázisnak megfelelő objektumok listái.
	/// </summary>
	class DB
	{
		internal List<Fact> facts = new List<Fact>();
		internal List<TaskType> tasktypes = new List<TaskType>();
		internal List<Interval> intevals = new List<Interval>();
		internal List<Task> tasks = new List<Task>();
		internal List<Teacher> teachers = new List<Teacher>();
		internal List<Skill> skills = new List<Skill>();
	}



	public class Engine : IEngine
	{

		#region IEngine Members

		/// <summary>
		/// Visszaadja a rendszerben található tantárgyak listáját.
		/// </summary>
		public List<IFact> Facts
		{
			get
			{
				List<IFact> list = new List<IFact>();

				using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
				{
					try
					{
						var q = from c in db.FACTs select c;
						foreach( var row in q )
						{
							Fact fact = new Fact( row );
							list.Add( fact );
						}
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt a tárgyak kilistázása közben." );
					}
				}
				return list;
			}
		}

		/// <summary>
		/// Létrehoz egy új tantárgyat.
		/// </summary>
		/// <param name="name">A tárgy neve. (ne legyen üres sztring)</param>
		/// <param name="neptun">A tárgy neptun kódja. (6 karakter)</param>
		/// <param name="description">A tárgy leírása.</param>
		public void CreateFact( string name, string neptun, string description )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{

					FACT fact = new FACT();
					fact.NAME = name;
					fact.NEPTUN = neptun;
					fact.DESCRIPTION = description;
					db.FACTs.InsertOnSubmit( fact );
					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba történt a tárgy létrehozásakor." );
				}
			}
		}

		/// <summary>
		/// Módosít egy tantárgyat.
		/// </summary>
		/// <param name="fact">A módosítandó tárgy.</param>
		/// <param name="name">A tárgy neve. (ne legyen üres sztring) </param>
		/// <param name="description">A tárgy leírása.</param>
		public void UpdateFact( IFact fact, string name, string description )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{

					FACT f = ( from row in db.FACTs where row.NEPTUN == fact.Neptun select row ).Single();
					f.NAME = name;
					f.DESCRIPTION = description;
					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Érvénytelen módosítási adatok" );
				}
			}
		}

		/// <summary>
		/// Eltávolít egy tantárgyat. Minden kapcsolódó tanszéki feladat és minden ahhoz kapcsolódó információ is törlődik.
		/// </summary>
		/// <param name="fact">A törlendő tárgy.</param>
		public void RemoveFact( IFact fact )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					FACT old = ( from row in db.FACTs where row.NEPTUN == fact.Neptun select row ).Single();
					cascadeDelete( old, db );

					SetStatus( db, AssignmentStatus.none );

					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba a tárgy törlésekor." );
				}
			}
		}

		/// <summary>
		/// Visszaadja a rendszerben található feladatok listáját.
		/// </summary>
		public List<ITask> Tasks
		{
			get
			{
				List<ITask> list = new List<ITask>();

				using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
				{
					try
					{
						foreach( Task t in GetDB( db ).tasks )
						{
							list.Add( t );
						}
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt a feladatok lekérdezése közben." );
					}
				}

				return list;
			}
		}

		/// <summary>
		/// Visszaadja a rendszerben található feladat típusok listáját.
		/// </summary>
		public List<ITaskType> TaskTypes
		{
			get
			{
				List<ITaskType> list = new List<ITaskType>();

				using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
				{
					try
					{
						var q = from c in db.TASKTYPEs select c;
						foreach( var row in q )
						{
							TaskType tt = new TaskType( row );
							list.Add( tt );
						}
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt a feladat típusok kilistázása közben." );
					}
				}

				return list;
			}
		}

		/// <summary>
		/// Visszaadja a rendszerben található szakmai preferenciák listáját.
		/// </summary>
		public List<ISkill> Skills
		{
			get
			{
				List<ISkill> list = new List<ISkill>();

				using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
				{
					try
					{
						var q = from c in db.SKILLs select c;
						foreach( var row in q )
						{
							Skill skill = new Skill( row );
							list.Add( skill );
						}
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt a szakmai preferenciák kilistázása közben." );
					}
				}

				return list;
			}
		}

		/// <summary>
		/// Létrehoz egy új szakmai preferenciát.
		/// </summary>
		/// <param name="name">A szakmai preferencia neve. (nem lehet üres sztring).</param>
		public void CreateSkill( string name )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{

					SKILL skill = new SKILL();
					skill.LABEL = name;
					db.SKILLs.InsertOnSubmit( skill );
					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba történt az új szakmai preferencia felvételekor." );
				}
			}
		}

		/// <summary>
		/// Eltávolít egy szakmai preferenciát. (És minden kapcsolódó adatot.)
		/// </summary>
		/// <param name="skill">A szakmai preferencia.</param>
		public void RemoveSkill( ISkill skill )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					SKILL old = ( from row in db.SKILLs where row.ID == skill.ID select row ).Single();
					cascadeDelete( old, db );

					SetStatus( db, AssignmentStatus.none );

					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba történt a szakmai preferencia törlésekor." );
				}
			}
		}

		/// <summary>
		/// Átnevez egy szakmai prefereciát.
		/// </summary>
		/// <param name="skill">A szakmai preferencia.</param>
		/// <param name="name">Az új név. (nem lehet üres sztring.)</param>
		public void RenameSkill( ISkill skill, string name )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{

					SKILL sk = ( from row in db.SKILLs where row.ID == skill.ID select row ).Single();
					sk.LABEL = name;
					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Érvénytelen módosítási adatok" );
				}
			}
		}

		/// <summary>
		/// Időintervallum kontruálása a megadott paraméterekből.
		/// </summary>
		/// <param name="day">A hét napja.</param>
		/// <param name="beginHour">Az intervallum eleje. (0 és 24 közötti szám, kisebb mint az intervallum vége.)</param>
		/// <param name="endHour">Az intervallum vége. (0 és 24 közötti szám, kisebb mint az intervallum vége.)</param>
		/// <param name="parity">Páros vagy páratlan hét. (true = páratlan) </param>
		/// <returns>A megadott paraméterekből konstruált intervallum.</returns>
		public IInterval ConstructInterval( DayOfWeek day, int beginHour, int endHour, bool parity )
		{
			Interval iv = new Interval();
			iv.Day = day;
			iv.BeginHour = beginHour;
			iv.EndHour = endHour;
			iv.Parity = parity;
			return iv;
		}

		/// <summary>
		/// Létrehoz egy új feladatot.
		/// </summary>
		/// <param name="fact">A kapcsolódó tantárgy.</param>
		/// <param name="name">A feladat neve. (nem lehet üres sztring.)</param>
		/// <param name="type">A feladat típusa. (intervallummal rendelkező típus.)</param>
		/// <param name="skills">A szükséges szakmai preferenciák.</param>
		/// <param name="intervals">A kapcsolódó intervallumok.</param>
		/// <param name="semester">A taskhoz tartozó szemeszter</param>
		public void CreateTask( IFact fact, string name, ITaskType type, List<ISkill> skills, List<IInterval> intervals, SemesterType semester )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				using( var scope = new TransactionScope() )
				{

					try
					{
						TASK task = new TASK();
						Task.UpdateLinqEntity( task, fact, name, 0, type, semester, db );
						db.TASKs.InsertOnSubmit( task );
						db.SubmitChanges();

						AddSkillListToTask( db, task.ID, skills );
						AddIntervalListToTask( db, task.ID, intervals );

						SetStatus( db, AssignmentStatus.none );
						db.SubmitChanges();
						scope.Complete();
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt a feladat létrehozása közben." );
					}
				}
			}
		}

		/// <summary>
		/// Létrehoz egy új feladatot.
		/// </summary>
		/// <param name="fact">A kapcsolódó tantárgy.</param>
		/// <param name="name">A feladat neve. (nem lehet üres szting.)</param>
		/// <param name="type">A feladat típusa. (intervallum nélküli típus.)</param>
		/// <param name="skills">A szükséges szakmai preferenciák.</param>
		/// <param name="hours">A tevékenység heti óraszáma</param>
		/// <param name="semester">A taskhoz tartozó szemeszter</param>
		public void CreateTask( IFact fact, string name, ITaskType type, List<ISkill> skills, int hours, SemesterType semester )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				using( var scope = new TransactionScope() )
				{

					try
					{
						TASK task = new TASK();
						Task.UpdateLinqEntity( task, fact, name, hours, type, semester, db );

						db.TASKs.InsertOnSubmit( task );
						db.SubmitChanges();

						AddSkillListToTask( db, task.ID, skills );

						db.SubmitChanges();
						scope.Complete();
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt a feladat létrehozása közben." );
					}
				}
			}
		}

		/// <summary>
		/// Módosít egy feladatot.
		/// </summary>
		/// <param name="task">A módosítandó feladat.</param>
		/// <param name="fact">A kapcsolódó tantárgy.</param>
		/// <param name="name">A feladat neve. (nem lehet üres sztring.)</param>
		/// <param name="type">A feladat típusa. (intervallummal rendelkező típus.)</param>
		/// <param name="skills">A szükséges szakmai preferenciák.</param>
		/// <param name="intervals">A kapcsolódó intervallumok.</param>
		/// <param name="semester">A taskhoz tartozó szemeszter</param>
		public void UpdateTask( ITask task, IFact fact, string name, ITaskType type, List<ISkill> skills, List<IInterval> intervals, SemesterType semester )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				using( var scope = new TransactionScope() )
				{

					try
					{
						TASK oldTask = ( from row in db.TASKs where row.ID == task.ID select row ).Single();

						if( oldTask.TEACHER != null )
						{
							SetStatus( db, AssignmentStatus.none );
						}

						RemoveOldtaskData( db, oldTask.ID );
						Task.UpdateLinqEntity( oldTask, fact, name, 0, type, semester, db );
						AddSkillListToTask( db, oldTask.ID, skills );
						AddIntervalListToTask( db, oldTask.ID, intervals );


						db.SubmitChanges();
						scope.Complete();
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt a feladat módosítása közben." );
					}
				}
			}
		}

		/// <summary>
		/// Módosít egy feladatot.
		/// </summary>
		/// <param name="task">A módosítandó feladat.</param>
		/// <param name="fact">A kapcsolódó tantárgy.</param>
		/// <param name="name">A feladat neve. (nem lehet üres sztring.)</param>
		/// <param name="type">A feladat típusa. (intervallum nélküli típus.)</param>
		/// <param name="skills">A szükséges szakmai preferenciák.</param>
		/// <param name="hours">A tevékenység heti óraszáma</param>
		/// <param name="semester">A taskhoz tartozó szemeszter</param>
		public void UpdateTask( ITask task, IFact fact, string name, ITaskType type, List<ISkill> skills, int hours, SemesterType semester )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				using( var scope = new TransactionScope() )
				{

					try
					{

						TASK oldTask = ( from row in db.TASKs where row.ID == task.ID select row ).Single();
						if( oldTask.TEACHER != null )
						{
							SetStatus( db, AssignmentStatus.none );
						}

						RemoveOldtaskData( db, oldTask.ID );
						Task.UpdateLinqEntity( oldTask, fact, name, hours, type, semester, db );
						AddSkillListToTask( db, oldTask.ID, skills );
						db.SubmitChanges();
						scope.Complete();
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt a feladat módosítása közben." );
					}
				}
			}
		}

		/// <summary>
		/// Töröl egy feladatot. (Minden kapcsolódó információ elvész!)
		/// </summary>
		/// <param name="task">A törlendő feladat.</param>
		public void RemoveTask( ITask task )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					TASK old = ( from row in db.TASKs where row.ID == task.ID select row ).Single();
					if( old.TEACHER != null )
					{
						SetStatus( db, AssignmentStatus.none );
					}

					cascadeDelete( old, db );
					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba történt a feladat törlése közben." );
				}
			}
		}

		/// <summary>
		/// Visszaadja a rendszer felhasználóinak listáját.
		/// </summary>
		public List<IUser> Users
		{
			get
			{
				List<IUser> list = new List<IUser>();

				using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
				{
					try
					{
						var q = from c in db.USER_DATAs select c;
						foreach( var row in q )
						{
							User usr = new User( row );
							list.Add( usr );
						}
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt a felhasználók kilistázása közben." );
					}
				}
				return list;
			}
		}

		/// <summary>
		/// Visszaadja a rendszerben lévő oktatók listáját.
		/// </summary>
		public List<ITeacher> Teachers
		{
			get
			{
				List<ITeacher> list = new List<ITeacher>();

				using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
				{
					try
					{
						foreach( Teacher t in GetDB( db ).teachers )
						{
							list.Add( t );
						}
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt az oktatók lekérdezése közben." );
					}
				}

				return list;
			}
		}

		/// <summary>
		/// Visszaadja a rendszeradminisztrátorok listáját.
		/// </summary>
		public List<IAdmin> Admins
		{
			get
			{
				List<IAdmin> list = new List<IAdmin>();

				using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
				{
					try
					{
						var q = from c in db.USER_DATAs where c.ROLE.Trim() == "admin" || c.ROLE.Trim() == "both" select c;
						foreach( var row in q )
						{
							Admin adm = new Admin( row );
							list.Add( adm );
						}
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt az adminisztrátorok kilistázása közben." );
					}
				}
				return list;
			}
		}

		/// <summary>
		/// Betölti a feladatok megjelenítéséhez és manipulálásához szükséges adatokat.
		/// </summary>
		/// <param name="facts">A tárgyak.</param>
		/// <param name="tasks">A feladatok.</param>
		/// <param name="taskTypes">A feladattípusok.</param>
		/// <param name="skills">A szakmai preferenciák.</param>
		public void LoadTaskData( out List<IFact> facts, out List<ITask> tasks, out List<ITaskType> taskTypes, out List<ISkill> skills )
		{
			facts = new List<IFact>();
			tasks = new List<ITask>();
			taskTypes = new List<ITaskType>();
			skills = new List<ISkill>();


			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					foreach( Fact f in GetDB( db ).facts )
					{
						facts.Add( f );
					}

					foreach( Task t in GetDB( db ).tasks )
					{
						tasks.Add( t );
					}

					foreach( TaskType tt in GetDB( db ).tasktypes )
					{
						taskTypes.Add( tt );
					}

					foreach( Skill s in GetDB( db ).skills )
					{
						skills.Add( s );
					}
				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba történt az adatok lekérdezése közben." );
				}
			}
		}

		/// <summary>
		/// Létrehoz egy új felhasználót.
		/// </summary>
		/// <param name="name">A felhasználó neve (nem lehet üres szting.).</param>
		/// <param name="pwHash">A felhasználó jelszavának lenyomata.</param>
		/// <param name="email">A felhasználó e-mail címe.</param>
		/// <param name="neptun">A felhasználó neptun kódja. (6 karakter)</param>
		/// <param name="isTeacher">Igaz, ha a felhasználó oktató.</param>
		/// <param name="isAdmin">Igaz, ha a felhasználó adminisztrátor.</param>
		public void CreateUser( string name, int pwHash, string email, string neptun, bool isTeacher, bool isAdmin )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{

					USER_DATA usr = new USER_DATA();

					usr.NEPTUN = neptun;
					if( isTeacher )
					{
						if( isAdmin )
						{
							usr.ROLE = "both";
						}
						else
						{
							usr.ROLE = "teacher";
						}
					}
					else
					{
						usr.ROLE = "admin";
					}

					usr.EMAIL = email;
					usr.PASSWORD = pwHash;
					usr.NAME = name;
					db.USER_DATAs.InsertOnSubmit( usr );
					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Már létezik egy felhasználó ugyanilyen neptun kóddal." );
				}
			}
		}

		/// <summary>
		/// Módosít egy felhasználót.
		/// </summary>
		/// <param name="user">A módosítandó felhasználó.</param>
		/// <param name="name">A felhasználó neve. (nem lehet üres sztring.)</param>
		/// <param name="pwHash">A felhasználó jelszavának lenyomata.</param>
		/// <param name="email">A felhasználó e-mail címe.</param>
		/// <param name="isTeacher">Igaz, ha a felhasználó oktató.</param>
		/// <param name="isAdmin">Igaz, ha a felhasználó adminisztrátor.</param>
		public void UpdateUser( IUser user, string name, int pwHash, string email, bool isTeacher, bool isAdmin )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					USER_DATA usr = ( from row in db.USER_DATAs where row.NEPTUN == user.Neptun select row ).Single();
					if( isTeacher )
					{
						if( isAdmin )
						{
							usr.ROLE = "both";
						}
						else
						{
							usr.ROLE = "teacher";
						}
					}
					else
					{
						if( usr.ROLE.Trim() != "admin" )
						{
							SetStatus( db, AssignmentStatus.none );
						}
						usr.ROLE = "admin";
					}

					usr.EMAIL = email;
					usr.PASSWORD = pwHash;
					usr.NAME = name;
					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Érvénytelen módosítási adatok" );
				}
			}
		}

		/// <summary>
		/// Módosít egy felhasználót.
		/// </summary>
		/// <param name="user">A módosítandó felhasználó.</param>
		/// <param name="name">A felhasználó neve. (nem lehet üres sztring.)</param>
		/// <param name="pwHash">A felhasználó jelszavának lenyomata.</param>
		/// <param name="email">A felhasználó e-mail címe.</param>
		public void UpdateUser( IUser user, string name, int pwHash, string email )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					USER_DATA usr = ( from row in db.USER_DATAs where row.NEPTUN == user.Neptun select row ).Single();
					usr.EMAIL = email;
					usr.PASSWORD = pwHash;
					usr.NAME = name;
					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Érvénytelen módosítási adatok" );
				}
			}
		}

		/// <summary>
		/// Beállítja egy oktató szakmai és személyes preferenciáit.
		/// </summary>
		/// <param name="teacher">Az oktató.</param>
		/// <param name="skills">A szakmai preferenciák listája.</param>
		/// <param name="preferences">A személyes preferenciák listája.</param>
		public void UpdatePreferences( ITeacher teacher, List<ISkill> skills, IList<ILike> preferences )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					USER_DATA ud = ( from row in db.USER_DATAs where row.NEPTUN == teacher.Neptun select row ).Single();
					db.USER_SKILLs.DeleteAllOnSubmit( db.USER_SKILLs.Where( us => us.USER_NEPTUN == teacher.Neptun ) );
					db.TASK_USER_LIKEs.DeleteAllOnSubmit( db.TASK_USER_LIKEs.Where( ul => ul.USER_NEPTUN == teacher.Neptun ) );

					foreach( ISkill sk in skills )
					{
						if( !( from row in db.SKILLs where row.ID == sk.ID select row ).Any() )
						{
							throw new EngineException( "Az oktatóhoz hozzárendelni kívánt szakmai preferencia nem létezik" );
						}

						USER_SKILL us = new USER_SKILL();
						us.USER_NEPTUN = teacher.Neptun;
						us.SKILL_ID = sk.ID;
						db.USER_SKILLs.InsertOnSubmit( us );
					}

					foreach( ILike like in preferences )
					{
						if( !( from row in db.TASKs where row.ID == like.Target.ID select row ).Any() )
						{
							throw new EngineException( "Az oktatóhoz hozzárendelni kívánt személyes preferencia nem létezik" );
						}

						TASK_USER_LIKE tul = new TASK_USER_LIKE();
						tul.USER_NEPTUN = teacher.Neptun;
						tul.TASK_ID = like.Target.ID;
						tul.STARS = like.Stars;
						db.TASK_USER_LIKEs.InsertOnSubmit( tul );
					}

					SetStatus( db, AssignmentStatus.none );
					db.SubmitChanges();

				}
				catch( Exception ex )
				{
					throw new EngineException( "Érvénytelen módosítási adatok" );
				}
			}
		}

		/// <summary>
		/// Létrehoz egy személyes preferencia objektumot.
		/// </summary>
		/// <param name="target">A tantárgy.</param>
		/// <param name="stars">A csillagok száma. (minimum 0, maximum 6)</param>
		/// <returns>Létrehoz egy személyes preferencia objektumot.</returns>
		public ILike ConstructLike( ITask target, int stars )
		{
			Like like = new Like();
			like.Target = target;
			like.Stars = stars;
			return like;
		}

		/// <summary>
		/// Eltávolít egy felhasználót a renszerből, annak minden kapcsolódó adatával.
		/// </summary>
		/// <param name="user">Az eltávolítandó felhasználó.</param>
		public void RemoveUser( IUser user )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					USER_DATA old = ( from row in db.USER_DATAs where row.NEPTUN == user.Neptun select row ).Single();
					if( old.ROLE.Trim() != "admin" )
					{
						SetStatus( db, AssignmentStatus.none );
					}
					cascadeDelete( old, db );
					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "A felhasználó nem létezik." );
				}
			}
		}

		/// <summary>
		/// Órarend kiküldése egy oktatónak.
		/// </summary>
		/// <param name="teacher">Az oktató.</param>
		public void SendTimeTable( ITeacher teacher )
		{
			try
			{
				MailEngine.SendMail( teacher.Email.Trim(), "[Terheléselosztás] Az órarend elkészült", MailEngine.DefaultMessage.seeTimeTable );
			}
			catch( Exception ex )
			{
				throw new EngineException( "Hiba történt a emailek kézbesítése közben." );
			}
		}

		/// <summary>
		/// Preferencia üzenet kiküldése egy oktatónak.
		/// </summary>
		/// <param name="teacher">Az oktató.</param>
		public void SendPreferenceMessage( ITeacher teacher )
		{
			try
			{
				MailEngine.SendMail( teacher.Email.Trim(), "[Terheléselosztás] Értesítés", MailEngine.DefaultMessage.setPreferences );
			}
			catch( Exception ex )
			{
				throw new EngineException( "Hiba történt a emailek kézbesítése közben." );
			}
		}

		/// <summary>
		/// Órarend kiküldése minden oktatónak.
		/// </summary>
		public void SendAllTimeTable()
		{
			try
			{
				foreach( ITeacher t in Teachers )
				{
					SendTimeTable( t );
				}
			}
			catch( Exception ex )
			{
				throw new EngineException( "Hiba történt a emailek kézbesítése közben." );
			}
		}

		/// <summary>
		/// Üzenet küldése egy oktatónak.
		/// </summary>
		/// <param name="user">Az oktató.</param>
		/// <param name="message">Az üzenet.</param>
		public void SendNotification( IUser user, string message )
		{
			try
			{
				MailEngine.SendMail( user.Email.Trim(), "[Terheléselosztás] Értesítés", message );
			}
			catch( Exception ex )
			{
				throw new EngineException( "Hiba történt a emailek kézbesítése közben." );
			}
		}

		/// <summary>
		/// Broadcast üzenet küldése.
		/// </summary>
		/// <param name="message">Az üzenet.</param>
		/// <param name="teachersOnly">Igaz, ha csak az oktatóknak küldjük el.</param>
		/// <param name="adminsOnly">Igaz, ha csak az adminoknak küldjük el.</param>
		public void SendBroadcastNotification( string message, bool teachersOnly, bool adminsOnly )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					var q = from row in db.USER_DATAs select row;
					foreach( USER_DATA ud in q )
					{
						if( ud.ROLE.Trim() == "both" )
						{
							MailEngine.SendMail( ud.EMAIL.Trim(), "[Terheléselosztás] Értesítés", message );
						}
						else if( teachersOnly && ud.ROLE.Trim() == "teacher" )
						{
							MailEngine.SendMail( ud.EMAIL.Trim(), "[Terheléselosztás] Értesítés", message );
						}
						else if( adminsOnly && ud.ROLE.Trim() == "admin" )
						{
							MailEngine.SendMail( ud.EMAIL.Trim(), "[Terheléselosztás] Értesítés", message );
						}
						else if( !adminsOnly && !teachersOnly )
						{
							MailEngine.SendMail( ud.EMAIL.Trim(), "[Terheléselosztás] Értesítés", message );
						}
					}
				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba történt a emailek kézbesítése közben." );
				}
			}

		}

		/// <summary>
		/// Broadcast üzenet küldése.
		/// </summary>
		/// <param name="msg">Az üzenet.</param>
		/// <param name="teachersOnly">Igaz, ha csak az oktatóknak küldjük el.</param>
		/// <param name="adminsOnly">Igaz, ha csak az adminoknak küldjük el.</param>
		public void SendBroadcastNotification( MailEngine.DefaultMessage msg, bool teachersOnly, bool adminsOnly )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					var q = from row in db.USER_DATAs select row;
					foreach( USER_DATA ud in q )
					{
						if( ud.ROLE.Trim() == "both" )
						{
							MailEngine.SendMail( ud.EMAIL.Trim(), "[Terheléselosztás] Értesítés", msg );
						}
						else if( teachersOnly && ud.ROLE.Trim() == "teacher" )
						{
							MailEngine.SendMail( ud.EMAIL.Trim(), "[Terheléselosztás] Értesítés", msg );
						}
						else if( adminsOnly && ud.ROLE.Trim() == "admin" )
						{
							MailEngine.SendMail( ud.EMAIL.Trim(), "[Terheléselosztás] Értesítés", msg );
						}
						else if( !adminsOnly && !teachersOnly )
						{
							MailEngine.SendMail( ud.EMAIL.Trim(), "[Terheléselosztás] Értesítés", msg );
						}
					}
				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba történt a emailek kézbesítése közben." );
				}
			}

		}

		/// <summary>
		/// Beosztás létrehozása a rendszerben lévő adatok alapján.
		/// </summary>
		/// <returns> A beosztás eredményessége.</returns>
		public IAssignmentResult CreateAssignment()
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					SemesterType currentSemester;

					var q = ( from c in db.GLOBAL_DATAs select c ).First();
					currentSemester = ConvertSemester( q.SEMESTER );

					DB data = GetDB( db );

					AssignmentEngine.Result result = AssignmentEngine.CreateAssignment( currentSemester, data.teachers, data.tasks );

					if( result.isSucceded )
					{
						foreach( int taskID in result.assignment.Keys )
						{
							TASK task = ( from row in db.TASKs where row.ID == taskID select row ).Single();
							string teacherNeptun;
							result.assignment.TryGetValue( taskID, out teacherNeptun );
							task.TEACHER = teacherNeptun;
						}

						SetStatus( db, AssignmentStatus.ok );
						db.SubmitChanges();
					}
					else
					{
						SetStatus( db, AssignmentStatus.failed );
						db.SubmitChanges();
					}

					return result;
				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba a beosztás létrehozása közben." );
				}
			}
		}

		/// <summary>
		/// A beosztás jelenlegi állapotatát adja vissza.
		/// </summary>
		public AssignmentStatus StatusOfAssignment
		{
			get
			{
				using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
				{
					try
					{
						var q = ( from c in db.GLOBAL_DATAs select c ).First();
						return ConvertStatus( q.STATUS );
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt az állapot lekérdezése közben." );
					}
				}

			}
		}

		/// <summary>
		/// Frissíti a beosztást.
		/// </summary>
		/// <param name="tasks">A feladatok listája.</param>
		public void UpdateAssignment( List<ITask> tasks )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					DB data = GetDB( db );

					foreach( ITask t1 in tasks )
					{
						foreach( ITask t2 in tasks )
						{
							// Task információk frissítése az adatbázis alapján
							ITask task1 = data.tasks.Single( t => t.ID == t1.ID );
							ITask task2 = data.tasks.Single( t => t.ID == t2.ID );

							if( ( t1.RelatedTeacher.Neptun == t2.RelatedTeacher.Neptun ) && ( task1.ID != task2.ID ) )
							{
								if( task1.HasIntersect( task2 ) )
								{
									throw new EngineException( "A megadott beosztás ütközést tartalmaz." );
								}
							}
						}
					}

					foreach( ITask task in tasks )
					{
						var q = ( from row in db.TASKs where row.ID == task.ID select row ).Single();
						string teacherNeptun = ( from row in db.USER_DATAs where row.NEPTUN == task.RelatedTeacher.Neptun select row.NEPTUN ).Single();
						q.TEACHER = teacherNeptun;
					}
					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba történt az állapot lekérdezése közben." );
				}
			}
		}

		/// <summary>
		/// Visszaadja egy oktató profilját.
		/// </summary>
		/// <param name="neptun">Az oktató neptun kódja.</param>
		/// <returns>Az oktató profilja.</returns>
		public ITeacher GetTeacherProfile( string neptun )
		{
			List<ITeacher> tl = Teachers;
			return tl.Single( t => t.Neptun == neptun );
		}

		/// <summary>
		/// Visszaadja egy admin profilját.
		/// </summary>
		/// <param name="neptun">Az admin neptun kódja.</param>
		/// <returns>Az admin profilja.</returns>
		public IAdmin GetAdminProfile( string neptun )
		{
			List<IAdmin> al = Admins;
			return al.Single( a => a.Neptun == neptun );
		}

		/// <summary>
		/// Megpróbálja beléptetni a felhasználót.
		/// </summary>
		/// <param name="neptun">Az azonosító.</param>
		/// <param name="passwordHash">A felhasználó jelszavának lenyomata.</param>
		/// <param name="roleIsTeacher">Igaz, ha a felhasználó oktató szerepben akar belépni.</param>
		/// <returns>Igaz, ha a belépés sikeres volt.</returns>
		public bool LoginUser( string neptun, int passwordHash, bool roleIsTeacher )
		{

			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					var q = ( from c in db.USER_DATAs where c.NEPTUN.Trim() == neptun.Trim() select c ).Single();

					if( q.ROLE.Trim() == "both" || ( roleIsTeacher && q.ROLE.Trim() == "teacher" ) || ( !roleIsTeacher && q.ROLE.Trim() == "admin" ) )
					{
						return q.PASSWORD == passwordHash;
					}
					else
					{
						return false;
					}


				}
				catch( Exception ex )
				{
					return false;
				}
			}


		}

		/// <summary>
		/// Az oktatók szakmai profiljának szerkesztését engedélyezi vagy letiltja.
		/// </summary>		
		public bool IsTeacherSettingsEnabled
		{
			get
			{
				try
				{
					using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
					{
						var q = ( from c in db.GLOBAL_DATAs select c ).First();
						if( q.TEACHERSETTINGSENABLED == 0 ) return false;
						else return true;
					}

				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba történt a lekérdezés közben." );
				}
			}
			set
			{
				try
				{
					using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
					{
						var q = ( from c in db.GLOBAL_DATAs select c ).First();
						if( value == true ) q.TEACHERSETTINGSENABLED = 1;
						else q.TEACHERSETTINGSENABLED = 0;
						db.SubmitChanges();
					}

				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba történt a módosítás közben." );
				}
			}
		}

		/// <summary>
		/// Váltás a szemeszterek között.
		/// </summary>
		public void SwitchSemester()
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					var q = ( from c in db.GLOBAL_DATAs select c ).First();
					if( q.SEMESTER.Trim() == "spring" )
					{
						q.SEMESTER = "autumn";
					}
					else
					{
						q.SEMESTER = "spring";
					}

					db.INTERVALs.DeleteAllOnSubmit( db.INTERVALs );

					var q2 = from c in db.TASKs select c;
					foreach( TASK t in q2 )
					{
						t.TEACHER = null;
					}
					SetStatus( db, AssignmentStatus.none );

					db.SubmitChanges();
				}
				catch( Exception ex )
				{
					throw new EngineException( "Hiba történt az szemeszter módosítása közben." );
				}
			}
		}


		/// <summary>
		/// A jelenlegi szemesztert kérdezi le.
		/// </summary>
		public SemesterType CurrentSemester
		{
			get
			{
				using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
				{
					try
					{
						var q = ( from c in db.GLOBAL_DATAs select c ).First();
						return ConvertSemester( q.SEMESTER );
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt az szemeszter lekérdezése közben." );
					}
				}
			}
		}

		/// <summary>
		/// Létrehozza a jelszó lenyomatát.
		/// </summary>
		/// <param name="password">A jelszó.</param>
		/// <returns>A jelszó lenyomata.</returns>
		public int CreateHash( string password )
		{
			return password.Trim().GetHashCode(); ;
		}

		/// <summary>
		/// Visszaadja a rendszerben található tantárgyak listáját.
		/// </summary>
		/// <returns>Visszaadja a rendszerben található tantárgyak listáját.</returns>
		public List<IFact> GetFacts()
		{
			return Facts;
		}

		/// <summary>
		/// Visszaadja a rendszerben található feladatok listáját.
		/// </summary>
		/// <returns>Visszaadja a rendszerben található feladatok listáját.</returns>
		public List<ITask> GetTasks()
		{
			return Tasks;
		}

		/// <summary>
		/// Visszaadja a rendszerben található feladatok listáját.
		/// </summary>
		/// <param name="currentSemesterOnly">True, ha csak az adott félévben érvényes taskok kellenek.</param>
		/// <returns>Visszaadja a rendszerben található feladatok listáját.</returns>
		public List<ITask> GetTasks( bool currentSemesterOnly )
		{
			if( !currentSemesterOnly )
			{
				return Tasks;
			}
			else
			{
				SemesterType currentSemester;
				using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
				{
					try
					{
						var q = ( from c in db.GLOBAL_DATAs select c ).First();
						currentSemester = ConvertSemester( q.SEMESTER );
						return Tasks.Where( t => t.Semester == CurrentSemester || t.Semester == SemesterType.both ).ToList();
					}
					catch( Exception ex )
					{
						throw new EngineException( "Hiba történt az szemeszter lekérdezése közben." );
					}
				}
			}

		}

		/// <summary>
		/// Visszaadja a rendszerben található feladat típusok listáját.
		/// </summary>
		/// <returns>Visszaadja a rendszerben található feladat típusok listáját.</returns>
		public List<ITaskType> GetTaskTypes()
		{
			return TaskTypes;
		}

		/// <summary>
		/// Visszaadja a rendszerben található szakmai preferenciák listáját.
		/// </summary>
		/// <returns>Visszaadja a rendszerben található szakmai preferenciák listáját.</returns>
		public List<ISkill> GetSkills()
		{
			return Skills;
		}

		/// <summary>
		/// Visszaadja a rendszer felhasználóinak listáját.
		/// </summary>
		/// <returns>Visszaadja a rendszer felhasználóinak listáját.</returns>
		public List<IUser> GetUsers()
		{
			return Users;
		}

		/// <summary>
		/// Visszaadja a rendszerben lévő oktatók listáját.
		/// </summary>
		/// <returns>Visszaadja a rendszerben lévő oktatók listáját.</returns>
		public List<ITeacher> GetTeachers()
		{
			return Teachers;
		}

		/// <summary>
		/// Visszaadja a rendszeradminisztrátorok listáját.
		/// </summary>
		/// <returns>Visszaadja a rendszeradminisztrátorok listáját.</returns>
		public List<IAdmin> GetAdmins()
		{
			return Admins;
		}

		/// <summary>
		/// Teszteli az üzleti logikát.
		/// </summary>
		private void Test0()
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					Console.WriteLine( "== Adding facts ==" );
					CreateFact( "testFact1", "123456", "test fact 1" );
					CreateFact( "testFact2", "ABCDEF", "test fact 2" );

					Console.WriteLine( "== Getting fact list ==" );
					foreach( IFact f in Facts )
					{
						Console.WriteLine( "name: {0}, neptun: {1}, description: {2}", f.Name.Trim(), f.Neptun.Trim(), f.Description.Trim() );
					}

					Console.WriteLine( "== Creating skills ==" );
					CreateSkill( "TestSkill1" );
					CreateSkill( "TestSkill2" );

					Console.WriteLine( "== Getting skills ==" );
					foreach( ISkill s in Skills )
					{
						Console.WriteLine( "id: {0}, name: {1}", s.ID, s.Name.Trim() );
					}

					Console.WriteLine( "== Adding tasks ==" );
					List<ISkill> sl = new List<ISkill>();
					sl.Add( Skills[0] );
					CreateTask( Facts[0], "TestTask1", TaskTypes[2], sl, 5, SemesterType.autumn );
					CreateTask( Facts[0], "TestTask2", TaskTypes[2], sl, 5, SemesterType.autumn );

					Console.WriteLine( "== Adding teachers ==" );
					CreateUser( "Dr. Professor", 42, "igazamvan@tuti.hu", "UGYUGY", true, false );
					List<ISkill> skillList = new List<ISkill>();
					List<ILike> likeList = new List<ILike>();
					skillList.Add( Skills[0] );
					skillList.Add( Skills[1] );
					UpdatePreferences( Teachers[0], skillList, likeList );

					Console.WriteLine( "== Generating assignment " );
					CreateAssignment();

					Console.WriteLine( "== Getting tasks ==" );
					foreach( ITask t in Tasks )
					{
						Console.WriteLine( "id: {0}, name: {1}, Fact: {2}, FirstSkill: {3}, Teacher: {4}", t.ID, t.Name.Trim(), t.RelatedFact.Name.Trim(), t.RequiredSkills[0].Name.Trim(), t.RelatedTeacher.Name );
					}

					Console.WriteLine( "== Done ==" );

				}
				catch( EngineException ex )
				{
					Console.WriteLine( "Hiba történt: {0}", ex.Message );
				}
			}
		}

		/// <summary>
		/// Teszteli az üzleti logikát.
		/// </summary>
		private void Test1()
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					CreateSkill( "skill1" );
					CreateSkill( "skill2" );
					CreateSkill( "skill3" );
					CreateSkill( "skill4" );
					CreateSkill( "skill5" );

					CreateUser( "teacher1", 0, "mail", "1", true, false );
					CreateUser( "teacher2", 0, "mail", "2", true, false );
					CreateUser( "teacher3", 0, "mail", "3", true, false );
					CreateUser( "teacher4", 0, "mail", "4", true, false );

					List<ISkill> sks = GetSkills();
					List<ISkill> skList1 = new List<ISkill>();
					skList1.Add( sks[0] );
					skList1.Add( sks[1] );
					skList1.Add( sks[2] );

					List<ISkill> skList2 = new List<ISkill>();
					skList2.Add( sks[1] );
					skList2.Add( sks[2] );
					skList2.Add( sks[3] );
					skList2.Add( sks[4] );

					List<ISkill> skList3 = new List<ISkill>();
					skList3.Add( sks[0] );
					skList3.Add( sks[1] );
					skList3.Add( sks[2] );

					List<ISkill> skList4 = new List<ISkill>();
					skList4.Add( sks[1] );
					skList4.Add( sks[2] );
					skList4.Add( sks[3] );

					CreateFact( "fact1", "f1", "desc" );
					CreateFact( "fact2", "f2", "desc" );

					IInterval iv1 = ConstructInterval( DayOfWeek.Monday, 12, 16, true );
					IInterval iv2 = ConstructInterval( DayOfWeek.Monday, 6, 8, true );
					IInterval iv3 = ConstructInterval( DayOfWeek.Thursday, 13, 15, true );
					IInterval iv4 = ConstructInterval( DayOfWeek.Friday, 13, 15, true );

					List<IInterval> ivList1 = new List<IInterval>();
					ivList1.Add( iv1 );
					ivList1.Add( iv2 );

					List<IInterval> ivList2 = new List<IInterval>();
					ivList1.Add( iv3 );
					ivList1.Add( iv4 );

					Console.WriteLine( "Creating tasks..." );

					CreateTask( Facts[0], "task1", TaskTypes.Single( tt => tt.Name.Trim() == "eloadás" ), skList3, ivList1, SemesterType.autumn );
					CreateTask( Facts[0], "task2", TaskTypes.Single( tt => tt.Name.Trim() == "gyakorlat" ), skList4, ivList1, SemesterType.autumn );
					CreateTask( Facts[1], "task3", TaskTypes.Single( tt => tt.Name.Trim() == "jegyzetírás" ), skList3, 10, SemesterType.autumn );
					CreateTask( Facts[1], "task4", TaskTypes.Single( tt => tt.Name.Trim() == "tematika kidolgozás" ), skList4, 10, SemesterType.autumn );

					List<ILike> likeList1 = new List<ILike>();
					List<ILike> likeList2 = new List<ILike>();
					List<ILike> likeList3 = new List<ILike>();
					List<ILike> likeList4 = new List<ILike>();

					ILike like1 = ConstructLike( Tasks.Single( t => t.ID == 1 ), 2 );
					ILike like2 = ConstructLike( Tasks.Single( t => t.ID == 2 ), 2 );
					ILike like3 = ConstructLike( Tasks.Single( t => t.ID == 3 ), 3 );
					ILike like4 = ConstructLike( Tasks.Single( t => t.ID == 4 ), 4 );

					likeList1.Add( like1 );
					likeList2.Add( like2 );
					likeList3.Add( like3 );
					likeList4.Add( like4 );

					Console.WriteLine( "Adding pregferences..." );

					UpdatePreferences( GetTeacherProfile( "1     " ), skList1, likeList1 );
					UpdatePreferences( GetTeacherProfile( "2     " ), skList1, likeList2 );
					UpdatePreferences( GetTeacherProfile( "3     " ), skList2, likeList3 );
					UpdatePreferences( GetTeacherProfile( "4     " ), skList2, likeList4 );

					Console.WriteLine( "Creating assingment..." );
					CreateAssignment();
					Console.WriteLine( "Done" );

					foreach( ITask t in GetTasks() )
					{
						Console.WriteLine( "{0}, {1}, {2}, {3}, {4}, {5}", t.ID, t.Name.Trim(), t.RelatedFact.Neptun.Trim(), t.RelatedTeacher.Name.Trim(), t.Semester, t.TimeIntervals );
					}

					Console.WriteLine( "Removing items" );

					RemoveFact( Facts.Single( f => f.Neptun.Trim() == "f1" ) );
					RemoveUser( Teachers.Single( t => t.Neptun.Trim() == "1" ) );
					RemoveUser( Teachers.Single( t => t.Neptun.Trim() == "2" ) );
					RemoveUser( Teachers.Single( t => t.Neptun.Trim() == "3" ) );
					RemoveUser( Teachers.Single( t => t.Neptun.Trim() == "4" ) );

					foreach( ITask t in GetTasks() )
					{
						Console.WriteLine( "{0}, {1}, {2}, {3}, {4}, {5}", t.ID, t.Name.Trim(), t.RelatedFact.Neptun.Trim(), t.RelatedTeacher, t.Semester, t.TimeIntervals );
					}


				}
				catch( EngineException ex )
				{
					Console.WriteLine( ex.Message );
				}
			}
		}

		/// <summary>
		/// Teszteli az üzleti logikát.
		/// </summary>
		private void Test2()
		{
			CreateUser( "Adam", 0, "horviadam@gmail.com", "123456", true, false );
			CreateUser( "Janos", 0, "riblijanos@gmail.com", "ABCDEF", true, false );
			SendAllTimeTable();
		}

		/// <summary>
		/// Teszteli az üzleti logikát.
		/// </summary>
		private void Test3()
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					CreateSkill( "skill1" );
					CreateSkill( "skill2" );
					CreateSkill( "skill3" );
					CreateSkill( "skill4" );
					CreateSkill( "skill5" );

					CreateUser( "teacher1", CreateHash( "almafa" ), "mail", "123456", true, true );
					CreateUser( "teacher2", CreateHash( "almafa" ), "mail", "234567", true, false );
					CreateUser( "teacher3", CreateHash( "almafa" ), "mail", "345678", true, false );
					CreateUser( "teacher4", CreateHash( "almafa" ), "mail", "456789", true, false );

					List<ISkill> sks = GetSkills();
					List<ISkill> skList1 = new List<ISkill>();
					skList1.Add( sks[0] );
					skList1.Add( sks[1] );
					skList1.Add( sks[2] );

					List<ISkill> skList2 = new List<ISkill>();
					skList2.Add( sks[1] );
					skList2.Add( sks[2] );
					skList2.Add( sks[3] );
					skList2.Add( sks[4] );

					List<ISkill> skList3 = new List<ISkill>();
					skList3.Add( sks[0] );
					skList3.Add( sks[1] );
					skList3.Add( sks[2] );

					List<ISkill> skList4 = new List<ISkill>();
					skList4.Add( sks[1] );
					skList4.Add( sks[2] );
					skList4.Add( sks[3] );

					CreateFact( "fact1", "f1", "desc" );
					CreateFact( "fact2", "f2", "desc" );

					IInterval iv1 = ConstructInterval( DayOfWeek.Monday, 12, 16, true );
					IInterval iv2 = ConstructInterval( DayOfWeek.Monday, 6, 8, true );
					IInterval iv3 = ConstructInterval( DayOfWeek.Thursday, 13, 15, true );
					IInterval iv4 = ConstructInterval( DayOfWeek.Friday, 13, 15, true );

					List<IInterval> ivList1 = new List<IInterval>();
					ivList1.Add( iv1 );
					ivList1.Add( iv2 );

					List<IInterval> ivList2 = new List<IInterval>();
					ivList1.Add( iv3 );
					ivList1.Add( iv4 );

					Console.WriteLine( "Creating tasks..." );

					CreateTask( Facts[0], "task1", TaskTypes.Single( tt => tt.Name.Trim() == "eloadás" ), skList3, ivList1, SemesterType.autumn );
					CreateTask( Facts[0], "task2", TaskTypes.Single( tt => tt.Name.Trim() == "gyakorlat" ), skList4, ivList1, SemesterType.autumn );
					CreateTask( Facts[1], "task3", TaskTypes.Single( tt => tt.Name.Trim() == "jegyzetírás" ), skList3, 10, SemesterType.autumn );
					CreateTask( Facts[1], "task4", TaskTypes.Single( tt => tt.Name.Trim() == "tematika kidolgozás" ), skList4, 10, SemesterType.autumn );

					List<ILike> likeList1 = new List<ILike>();
					List<ILike> likeList2 = new List<ILike>();
					List<ILike> likeList3 = new List<ILike>();
					List<ILike> likeList4 = new List<ILike>();

					ILike like1 = ConstructLike( Tasks.Single( t => t.ID == 1 ), 2 );
					ILike like2 = ConstructLike( Tasks.Single( t => t.ID == 2 ), 2 );
					ILike like3 = ConstructLike( Tasks.Single( t => t.ID == 3 ), 3 );
					ILike like4 = ConstructLike( Tasks.Single( t => t.ID == 4 ), 4 );

					likeList1.Add( like1 );
					likeList2.Add( like2 );
					likeList3.Add( like3 );
					likeList4.Add( like4 );

					Console.WriteLine( "Adding pregferences..." );

					UpdatePreferences( GetTeacherProfile( "123456" ), skList1, likeList1 );
					UpdatePreferences( GetTeacherProfile( "234567" ), skList1, likeList2 );
					UpdatePreferences( GetTeacherProfile( "345678" ), skList2, likeList3 );
					UpdatePreferences( GetTeacherProfile( "456789" ), skList2, likeList4 );

					Console.WriteLine( "Creating assingment..." );
					CreateAssignment();
					Console.WriteLine( "Done" );

					foreach( ITask t in GetTasks() )
					{
						Console.WriteLine( "{0}, {1}, {2}, {3}, {4}, {5}", t.ID, t.Name.Trim(), t.RelatedFact.Neptun.Trim(), t.RelatedTeacher.Name.Trim(), t.Semester, t.TimeIntervals );
					}
				}
				catch( EngineException ex )
				{
					Console.WriteLine( ex.Message );
				}
			}
		}

		/// <summary>
		/// Teszteli az üzleti logikát.
		/// </summary>
		/// <param name="nbr">A teszt sorszáma.</param>
		public void Test( int nbr )
		{
			switch( nbr )
			{
				case 0:
					Test0();
					break;
				case 1:
					Test1();
					break;
				case 2:
					Test2();
					break;
				case 3:
					Test3();
					break;
				default:
					break;
			}

		}

		/// Beállítja a holnapot.
		/// </summary>
		/// <param name="page">A holnap ahol az alkalmazás fut.</param>
		public void SetProgramPage( string page )
		{
			MailEngine.Programhomepage = page;
		}

		/// <summary>
		/// A megadott sztringet szemeszter típussá konvertálja.
		/// </summary>
		/// <param name="s">A sztring.</param>
		/// <returns>A szemeszter típus.</returns>
		public SemesterType ConvertSemester( string s )
		{
			if( s.Trim() == "autumn" )
			{
				return SemesterType.autumn;
			}
			else if( s.Trim() == "spring" )
			{
				return SemesterType.spring;
			}
			else
			{
				return SemesterType.both;
			}
		}

		/// <summary>
		/// A megadott sztringet a hét napjává konvertálja.
		/// </summary>
		/// <param name="s">A sztring.</param>
		/// <returns>A hét napja.</returns>
		public DayOfWeek ConvertDay( string s )
		{
			if( s.Trim() == "Mon" )
			{
				return DayOfWeek.Monday;
			}
			else if( s.Trim() == "Tue" )
			{
				return DayOfWeek.Tuesday;
			}
			else if( s.Trim() == "Wed" )
			{
				return DayOfWeek.Wednesday;
			}
			else if( s.Trim() == "Thu" )
			{
				return DayOfWeek.Thursday;
			}
			else if( s.Trim() == "Fri" )
			{
				return DayOfWeek.Friday;
			}
			else if( s.Trim() == "Sat" )
			{
				return DayOfWeek.Saturday;
			}
			else
			{
				return DayOfWeek.Sunday;
			}
		}

		/// <summary>
		/// A megadott sztringet beosztás állapottá konvertálja.
		/// </summary>
		/// <param name="s">A sztring.</param>
		/// <returns>A beosztás állapota.</returns>
		public AssignmentStatus ConvertStatus( string s )
		{
			if( s.Trim() == "ok" )
			{
				return AssignmentStatus.ok;
			}
			else if( s.Trim() == "failed" )
			{
				return AssignmentStatus.failed;
			}
			else
			{
				return AssignmentStatus.none;
			}
		}

		/// <summary>
		/// A megadott szemesztert szringgé alakítja.
		/// </summary>
		/// <param name="st">A szemeszter.</param>
		/// <returns>A sztring.</returns>
		public string Convert( SemesterType st )
		{
			if( st == SemesterType.autumn )
			{
				return "autumn";
			}
			else if( st == SemesterType.spring )
			{
				return "spring";
			}
			else
			{
				return "both";
			}
		}

		/// <summary>
		/// A megadott napot sztringé alakítja.
		/// </summary>
		/// <param name="dow">A nap.</param>
		/// <returns>A sztring.</returns>
		public string Convert( DayOfWeek dow )
		{
			return dow.ToString().Substring( 0, 3 );
		}

		/// <summary>
		/// A megadott beosztás állapotot sztringgél alakítja.
		/// </summary>
		/// <param name="status">A beosztás állapota.</param>
		/// <returns>A sztring.</returns>
		public string Convert( AssignmentStatus status )
		{
			if( status == AssignmentStatus.ok )
			{
				return "ok";
			}
			else if( status == AssignmentStatus.failed )
			{
				return "failed";
			}
			else
			{
				return "none";
			}

		}

		/// <summary>
		/// A megadott szerepet sztringgé konvertálja.
		/// </summary>
		/// <param name="r">A szerep</param>
		/// <returns>A sztring.</returns
		public string Convert( Role r )
		{
			if( r == Role.user )
			{
				return "user";
			}
			else if( r == Role.admin )
			{
				return "admin";
			}
			else if( r == Role.teacher )
			{
				return "teacher";
			}
			else
			{
				return "both";
			}
		}

		/// <summary>
		/// A megadott sztringet szereppé konvertálja.
		/// </summary>
		/// <param name="s">A sztring.</param>
		/// <returns>A szerep</returns>
		public Role ConvertRole( string s )
		{
			if( s.Trim() == "user" )
			{
				return Role.user;
			}
			else if( s.Trim() == "admin" )
			{
				return Role.admin;
			}
			else if( s.Trim() == "teacher" )
			{
				return Role.teacher;
			}
			else
			{
				return Role.both;
			}
		}

		/// <summary>
		/// Igaz, ha a megadott neptunkódú felhasználónak van admin jogosultsága.
		/// </summary>
		/// <param name="visitorName">A felhasználó neptun kódja</param>
		/// <returns>Igaz, ha a megadott neptunkódú felhasználónak van admin jogosultsága.</returns>
		public bool IsAdmin( string visitorName )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					var q = ( from c in db.USER_DATAs where c.NEPTUN.Trim() == visitorName.Trim() select c ).Single();
					Role r = ConvertRole( q.ROLE );
					return ( r == Role.admin || r == Role.both );
				}
				catch( Exception ex )
				{
					return false;
				}
			}
		}

		/// <summary>
		/// Igaz, ha a megadott neptunkódú felhasználónak van oktatói jogosultsága.
		/// </summary>
		/// <param name="visitorName">A felhasználó neptun kódja</param>
		/// <returns>Igaz, ha a megadott neptunkódú felhasználónak van oktatói jogosultsága.</returns>
		public bool IsTeacher( string visitorName )
		{
			using( TerhelesElosztoDBClassesDataContext db = new TerhelesElosztoDBClassesDataContext() )
			{
				try
				{
					var q = ( from c in db.USER_DATAs where c.NEPTUN.Trim() == visitorName.Trim() select c ).Single();
					Role r = ConvertRole( q.ROLE );
					return ( r == Role.teacher || r == Role.both );
				}
				catch( Exception ex )
				{
					return false;
				}
			}
		}

		#endregion

		/// <summary>
		/// Singleton példány.
		/// </summary>
		private static Engine instance = new Engine();

		/// <summary>
		/// Singleton példány lekérdezése.
		/// </summary>
		/// <returns>Singleton példány.</returns>
		public static IEngine Instance()
		{
			return instance;
		}

		/// <summary>
		/// Igaz, ha a megadott oktató szakmailag kompetens a megadott tárgyhoz.
		/// </summary>
		/// <param name="user">Az oktató</param>
		/// <param name="task">A tárgy-</param>
		/// <returns>Igaz, ha a megadott oktató szakmailag kompetens a megadott tárgyhoz.</returns>
		private bool Competent( USER_DATA user, TASK task )
		{
			List<int> requiredSkillIDs = new List<int>();
			List<int> availableSkillIDs = new List<int>();

			if( user.ROLE.Trim() == "admin" )
			{
				return false;
			}

			foreach( TASK_SKILL sk in task.TASK_SKILLs )
			{
				requiredSkillIDs.Add( sk.SKILL_ID );
			}

			foreach( USER_SKILL sk in user.USER_SKILLs )
			{
				availableSkillIDs.Add( sk.SKILL_ID );
			}

			return !requiredSkillIDs.Except<int>( availableSkillIDs ).Any();

		}

		/// <summary>
		/// Betölti a legtöbb tábla adatát, de az objektumok között nem állítja be a táblák szerinti kapcsolatokat.
		/// </summary>
		/// <param name="db">Az adatbáziskontextus.</param>
		/// <returns>Az objektumok.</returns>
		private DB GetPrimitiveData( TerhelesElosztoDBClassesDataContext db )
		{
			DB r = new DB();

			var q = from row in db.FACTs select row;
			foreach( var row in q )
			{
				r.facts.Add( new Fact( row ) );
			}

			var q2 = from row in db.TASKTYPEs select row;
			foreach( var row in q2 )
			{
				r.tasktypes.Add( new TaskType( row ) );
			}

			var q3 = from row in db.INTERVALs select row;
			foreach( var row in q3 )
			{
				r.intevals.Add( new Interval( row ) );
			}

			var q4 = from row in db.TASKs select row;
			foreach( var row in q4 )
			{
				r.tasks.Add( new Task( row ) );
			}

			var q5 = from row in db.USER_DATAs where ( row.ROLE.Trim() == "teacher" || row.ROLE.Trim() == "both" ) select row;
			foreach( var row in q5 )
			{
				r.teachers.Add( new Teacher( row ) );
			}

			var q6 = from row in db.SKILLs select row;
			foreach( var row in q6 )
			{
				r.skills.Add( new Skill( row ) );
			}

			return r;
		}

		/// <summary>
		/// Betölti a legtöbb tábla adatát és beállítja az objektumok között a táblák szerinti kapcsolatot.
		/// </summary>
		/// <param name="db">Az adatbáziskontextus.</param>
		/// <returns>Az objektumok.</returns>
		private DB GetDB( TerhelesElosztoDBClassesDataContext db )
		{
			DB r = GetPrimitiveData( db );

			//Interval
			foreach( Interval interval in r.intevals )
			{
				//RelatedTask
				var q = from row in db.INTERVALs where row.ID == interval.ID select row.TASK_ID;
				interval.RealtedTask = r.tasks.Single( t => t.ID == q.Single() );
			}

			//Task
			foreach( Task task in r.tasks )
			{
				TASK t = ( from row in db.TASKs where row.ID == task.ID select row ).Single();

				//RelatedFact								
				task.RelatedFact = r.facts.Single( f => f.Neptun == t.FACT_NEPTUN );

				//Type
				task.Type = r.tasktypes.Single( tt => tt.ID == t.TYPE );

				//TimeIntervals
				task.TimeIntervals = r.intevals.Where( i => i.RealtedTask == task ).ToList<IInterval>();

				//RequiredSkills
				task.RequiredSkills = new List<ISkill>();
				foreach( TASK_SKILL ts in t.TASK_SKILLs )
				{
					task.RequiredSkills.Add( r.skills.Single( sk => sk.ID == ts.SKILL_ID ) );
				}

				//PossibleTeachers
				task.PossibleTeachers = new List<ITeacher>();
				var q = from row in db.USER_DATAs select row;
				List<USER_DATA> lq = new List<USER_DATA>();
				foreach( USER_DATA ud in q )
				{
					if( Competent( ud, t ) ) lq.Add( ud );
				}


				foreach( USER_DATA competentTeacher in lq )
				{
					task.PossibleTeachers.Add( r.teachers.Single( ta => ta.Neptun == competentTeacher.NEPTUN ) );
				}

				//RelatedTeacher
				task.RelatedTeacher = r.teachers.SingleOrDefault( ta => ta.Neptun == t.TEACHER );
			}

			//Teacher
			foreach( Teacher teacher in r.teachers )
			{
				USER_DATA ud = ( from row in db.USER_DATAs where row.NEPTUN == teacher.Neptun select row ).Single();

				//Skills
				teacher.Skills = new List<ISkill>();
				foreach( USER_SKILL us in ud.USER_SKILLs )
				{
					teacher.Skills.Add( r.skills.Single( sk => sk.ID == us.SKILL_ID ) );
				}

				//Personalpreferences
				teacher.PersonalPreferences = new List<ILike>();
				foreach( TASK_USER_LIKE tul in ud.TASK_USER_LIKEs )
				{
					Like like = new Like( tul );
					like.Target = r.tasks.Single( t => t.ID == tul.TASK_ID );
					teacher.PersonalPreferences.Add( like );
				}

				//Acceptabletasks
				teacher.AcceptableTasks = r.tasks.Where( t => t.PossibleTeachers.Contains( teacher ) ).ToList<ITask>();

				//Personaltimetable
				TimeTable tt = new TimeTable();
				tt.TaskList = r.intevals.Where( i => i.RealtedTask.RelatedTeacher == teacher ).ToList<IInterval>();
				teacher.PersonalTimeTable = tt;

			}

			return r;
		}

		/// <summary>
		/// Felveszi a megadott skilleket a megadott azonosítójú taskhoz.
		/// </summary>
		/// <param name="db">Az adatbáziskontesztus.</param>
		/// <param name="taskID">A task azonosítója.</param>
		/// <param name="skills">A skill lista-</param>
		private void AddSkillListToTask( TerhelesElosztoDBClassesDataContext db, int taskID, List<ISkill> skills )
		{
			foreach( ISkill sk in skills )
			{
				if( !( from row in db.SKILLs where row.ID == sk.ID select row ).Any() )
				{
					throw new EngineException( "A feladathoz hozzárendelni kívánt szakmai preferencia nem létezik" );
				}

				TASK_SKILL ts = new TASK_SKILL();
				ts.SKILL_ID = sk.ID;
				ts.TASK_ID = taskID;
				db.TASK_SKILLs.InsertOnSubmit( ts );
			}

		}

		/// <summary>
		/// Felveszi a megadott intervallumokat a megadott azonosítójú taskhoz.
		/// </summary>
		/// <param name="db">Az adatbáziskontextus.</param>
		/// <param name="taskID">A task azonosítója.</param>
		/// <param name="intervals">Az intervallumok.</param>
		private void AddIntervalListToTask( TerhelesElosztoDBClassesDataContext db, int taskID, List<IInterval> intervals )
		{
			foreach( IInterval iv in intervals )
			{
				INTERVAL i = new INTERVAL();
				i.BEGINHOUR = iv.BeginHour;
				i.ENDHOUR = iv.EndHour;

				if( iv.Parity )
				{
					i.PAIRITY = 1;
				}
				else
				{
					i.PAIRITY = 0;
				}

				i.WEEK_DAY = Convert( iv.Day );
				i.TASK_ID = taskID;
				db.INTERVALs.InsertOnSubmit( i );
			}
		}

		/// <summary>
		/// Beállítja a beosztás állapotát.
		/// </summary>
		/// <param name="db">Az adatbáziskontextus.</param>
		/// <param name="st">Az új állapot.</param>
		private void SetStatus( TerhelesElosztoDBClassesDataContext db, AssignmentStatus st )
		{
			try
			{
				var q = ( from c in db.GLOBAL_DATAs select c ).First();
				q.STATUS = Convert( st );
			}
			catch( Exception ex )
			{
				throw new EngineException( "Hiba történt az állapot beállítása közben." );
			}
		}

		/// <summary>
		/// Eltávolítja a taskhoz tartozó intervallumokat és skilleket.
		/// </summary>
		/// <param name="db">Az adatbáziskontextus-</param>
		/// <param name="taskID">A task azonosítója.</param>
		private void RemoveOldtaskData( TerhelesElosztoDBClassesDataContext db, int taskID )
		{
			foreach( INTERVAL iv in db.INTERVALs )
			{
				if( iv.TASK_ID == taskID ) db.INTERVALs.DeleteOnSubmit( iv );
			}

			foreach( TASK_SKILL ts in db.TASK_SKILLs )
			{
				if( ts.TASK_ID == taskID ) db.TASK_SKILLs.DeleteOnSubmit( ts );
			}
		}

		/// <summary>
		/// Eltávolítja a tárgyat minden kapcsoló adatával együtt.
		/// </summary>
		/// <param name="fact">A tárgy.</param>
		/// <param name="db">Az adatbáziskontextus.</param>
		private void cascadeDelete( FACT fact, TerhelesElosztoDBClassesDataContext db )
		{
			var q = from row in db.TASKs where row.FACT_NEPTUN == fact.NEPTUN select row;
			foreach( TASK task in q )
			{
				cascadeDelete( task, db );
			}
			db.FACTs.DeleteOnSubmit( fact );

		}

		/// <summary>
		/// Eltávolítja a feladatot minden kapcsolódó adatával együtt.
		/// </summary>
		/// <param name="task">A feladat.</param>
		/// <param name="db">Az adatbáziskontextus.</param>
		private void cascadeDelete( TASK task, TerhelesElosztoDBClassesDataContext db )
		{

			RemoveOldtaskData( db, task.ID );
			foreach( TASK_USER_LIKE tul in db.TASK_USER_LIKEs )
			{
				if( tul.TASK_ID == task.ID )
				{
					db.TASK_USER_LIKEs.DeleteOnSubmit( tul );
				}
			}
			db.TASKs.DeleteOnSubmit( task );
		}

		/// <summary>
		/// Eltávolítja a skillt minden kapcsolódó adatával együtt.
		/// </summary>
		/// <param name="skill">A skill.</param>
		/// <param name="db">Az adatbáziskontextus.</param>
		private void cascadeDelete( SKILL skill, TerhelesElosztoDBClassesDataContext db )
		{
			foreach( TASK_SKILL ts in db.TASK_SKILLs )
			{
				if( ts.SKILL_ID == skill.ID )
				{
					db.TASK_SKILLs.DeleteOnSubmit( ts );
				}
			}

			foreach( USER_SKILL us in db.USER_SKILLs )
			{
				if( us.SKILL_ID == skill.ID )
				{
					db.USER_SKILLs.DeleteOnSubmit( us );
				}
			}
			db.SKILLs.DeleteOnSubmit( skill );
		}

		/// <summary>
		/// Eltávolítja a feladattípust minden kapcsolódó adatával együtt.
		/// </summary>
		/// <param name="taskType">A feladattípus.</param>
		/// <param name="db">Az adatbáziskontextus.</param>
		private void cascadeDelete( TASKTYPE taskType, TerhelesElosztoDBClassesDataContext db )
		{
			var q = from row in db.TASKs where row.TYPE == taskType.ID select row;
			foreach( TASK task in q )
			{
				cascadeDelete( task, db );
			}
			db.TASKTYPEs.DeleteOnSubmit( taskType );
		}

		/// <summary>
		/// Eltávolítja a felhasználót minden kapcsolódó adatával együtt.
		/// </summary>
		/// <param name="user">A felhasználó.</param>
		/// <param name="db">Az adatbáziskontextus.</param>
		private void cascadeDelete( USER_DATA user, TerhelesElosztoDBClassesDataContext db )
		{
			foreach( USER_SKILL us in db.USER_SKILLs )
			{
				if( us.USER_NEPTUN == user.NEPTUN )
				{
					db.USER_SKILLs.DeleteOnSubmit( us );
				}
			}

			foreach( TASK_USER_LIKE ts in db.TASK_USER_LIKEs )
			{
				if( ts.USER_NEPTUN == user.NEPTUN )
				{
					db.TASK_USER_LIKEs.DeleteOnSubmit( ts );
				}
			}

			foreach( TASK t in db.TASKs )
			{
				if( t.TEACHER == user.NEPTUN )
				{
					t.TEACHER = null;
				}
			}
			db.USER_DATAs.DeleteOnSubmit( user );
		}



	}
}
